"""
固件下载控制器模块
协调整个固件下载流程，提供统一的下载接口
"""
import time
from typing import Tuple, Optional, Callable
from .transport_interface import ITransport, MockTransport
from .xcp_protocol import XcpProtocol
from .firmware_manager import FirmwareManager, FirmwareSegment, firm_checksum
from .config import g_config, XcpConfig, CanConfig, DownloadConfig
from app.utils.log import Log

class DownloadController:
    """固件下载控制器"""
    
    def __init__(self, transport: Optional[ITransport] = None):
        # Log.Open(True)
        self.log = Log("DownloadController")
        # 初始化各个组件
        self._transport = transport or MockTransport()
        self._xcp = XcpProtocol(self._transport, g_config.xcp)
        self._firmware_mgr = FirmwareManager()
        
        # 状态变量
        self._download_progress = 0.0
        self._current_segment = 0
        self._total_segments = 0
        self._progress_callback: Optional[Callable[[float, str], None]] = None
        
        self._is_connecting = False
        
    def set_progress_callback(self, callback: Callable[[float, str], None]) -> None:
        """设置进度回调函数"""
        self._progress_callback = callback
        
    def _update_progress(self, progress: float, message: str) -> None:
        """更新进度"""
        self._download_progress = progress
        if self._progress_callback:
            self._progress_callback(progress, message)
    
    def init_system(self, can_config: CanConfig) -> Tuple[bool, str]:
        """
        初始化系统
        
        Args:
            can_config: CAN配置参数
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            # 初始化传输层
            suc, msg = self._transport.open(
                can_config.baudrate, 
                can_config.device_channel
            )
            if not suc:
                return False, f"初始化传输层失败: {msg}"
            
            # 设置CAN ID
            self._xcp.set_can_ids(can_config.tx_id, can_config.rx_id)
            if hasattr(self._transport, 'set_can_ids'):
                self._transport.set_can_ids(can_config.tx_id, can_config.rx_id)
            
            return True, "系统初始化成功"
            
        except Exception as e:
            return False, f"系统初始化异常: {str(e)}"
    
    def deinit_system(self) -> Tuple[bool, str]:
        """
        反初始化系统
        
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            suc, msg = self._xcp.disconnect()
            if not suc:
                return False, f"断开连接失败: {msg}"
            
            # 反初始化传输层
            suc, msg = self._transport.close()
            if not suc:
                return False, f"反初始化传输层失败: {msg}"
            
            return True, "系统反初始化成功"
            
        except Exception as e:
            return False, f"系统反初始化异常: {str(e)}"
    
    def load_firmware(self, file_path: str) -> Tuple[bool, str]:
        """
        加载固件文件
        
        Args:
            file_path: 固件文件路径
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            # 根据文件扩展名选择解析器
            if file_path.lower().endswith('.s19'):
                suc, msg = self._firmware_mgr.load_srec_file(file_path)
            elif file_path.lower().endswith('.hex'):
                suc, msg = self._firmware_mgr.load_hex_file(file_path)
            else:
                return False, "不支持的固件文件格式，仅支持.s19和.hex格式"
            
            if not suc:
                return False, msg
            
            self._total_segments = self._firmware_mgr.get_segment_count()
            return True, f"固件加载成功: {msg}"
            
        except Exception as e:
            return False, f"加载固件异常: {str(e)}"
    
    def connect_target(self, timeout: int = 10) -> Tuple[bool, str]:
        """
        连接目标设备
        
        Args:
            timeout: 重试时间
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            self._update_progress(0.0, "正在连接目标设备...")
            
            self._is_connecting = False
            time_start = time.time()
            while time.time() - time_start < timeout:
            
                self._is_connecting = True
                # 4. 连接目标设备
                self.log.INFO("\n4. 连接目标设备...")
                suc, msg = self._xcp.connect(g_config.xcp.connect_mode)
                if not suc:
                    self.log.INFO(f"连接失败: {msg}")
                    suc, msg = self._xcp.reboot_target_app()
                    if suc:
                        self.log.INFO(f"重启目标设备成功: {msg}")
                        
                        self.log.INFO("重启目标设备成功，等待0.1秒后连接目标设备")
                        time.sleep(0.1)
                        continue
                    else:
                        self.log.INFO(f"重启目标设备失败: {msg}")
                else:         
                    self.log.INFO(f"连接成功: {msg}")
                    self._is_connecting = False
                    return True, "ok"
                    
                time.sleep(0.8)
            
            # for attempt in range(retry_count):
            #     suc, msg = self._xcp.connect(g_config.xcp.connect_mode)
            #     if suc:
            #         self._update_progress(10.0, f"连接成功: {msg}")
            #         return True, msg
                
            #     if attempt < retry_count - 1:
            #         self._update_progress(
            #             5.0 + attempt * 2.0, 
            #             f"连接失败，正在重试... ({attempt + 1}/{retry_count})"
            #         )
            #         time.sleep(1)
            
            return False, f"连接失败，超时时间{timeout}s"
            
        except Exception as e:
            return False, f"连接过程异常: {str(e)}"
        
    def disconnect_target(self) -> Tuple[bool, str]:
        """
        断开目标设备
        """
        try:
            suc, msg = self._xcp.disconnect()
            return suc, msg 
        except Exception as e:
            return False, f"断开连接过程异常: {str(e)}"
        
    
    def download_firmware(self) -> Tuple[bool, str]:
        """
        下载固件到目标设备
        
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._xcp.is_connected():
                return False, "XCP未连接"
            
            if not self._firmware_mgr.is_loaded():
                return False, "固件未加载"
            
            # 开始编程会话
            self._update_progress(10.0, "开始编程会话...")
            suc, msg = self._xcp.program_start()
            if not suc:
                return False, f"开始编程会话失败: {msg}"
            
            # 获取所有固件段
            segments = self._firmware_mgr.get_segments()
            total_size = self._firmware_mgr.get_total_size()
            processed_size = 0
            
            # 第一步：擦除所有段
            self._update_progress(15.0, "正在擦除存储器...")
            # for i, segment in enumerate(segments):
            #     suc, msg = self._erase_segment(segment)
            #     if not suc:
            #         return False, f"擦除段{i+1}失败: {msg}"
                
            #     progress = 15.0 + (i + 1) * 25.0 / len(segments)
            #     self._update_progress(progress, f"擦除段{i+1}/{len(segments)}")
            suc, msg = self._erase_segment(segments[0])
            if not suc:
                return False, f"擦除段{i+1}失败: {msg}"
        
            # 第二步：编程所有段
            self._update_progress(40.0, "正在编程固件...")
            for i, segment in enumerate(segments):
                self._current_segment = i + 1
                suc, msg = self._program_segment(segment, processed_size, total_size)
                if not suc:
                    return False, f"编程段{i+1}失败: {msg}"
                
                processed_size += segment.size
                progress = 40.0 + processed_size * 50.0 / total_size
                self._update_progress(progress, f"编程段{i+1}/{len(segments)}")
            
            suc, msg = self._flash_done()
            if not suc:
                return False, f"编程完成失败: {msg}"
            
            time.sleep(0.1)
            
            # # 第三步：校验固件（可选）
            if g_config.download.verify_enable:
                self._update_progress(90.0, "正在校验固件...")
                # 6. 校验checksum
                self.log.INFO("\n6. 校验固件checksum...")
                suc, msg = self._verify_checksum()
                if suc:
                    self.log.INFO(f"Checksum校验成功: {msg}")
                else:
                    self.log.WARN(f"Checksum校验失败: {msg}")
            
            time.sleep(0.1)
            
            # # 第四步：复位目标设备（可选）
            if g_config.download.auto_reset:
                self._update_progress(95.0, "正在复位目标设备...")
                suc, msg = self._xcp.program_reset()
                if not suc:
                    return False, f"复位失败: {msg}"
            
            self._update_progress(100.0, "固件下载完成!")
            return True, "固件下载成功"
            
        except Exception as e:
            return False, f"下载过程异常: {str(e)}"
        
    def _reboot_target_app(self) -> Tuple[bool, str]:
        """重启目标设备，让目标设备进入boot模式中"""
        try:
            suc, msg = self._xcp.reboot_target_app()
            if not suc:
                return False, f"重启目标设备失败: {msg}"
            return True, "重启目标设备成功"
        except Exception as e:
            return False, f"重启目标设备过程异常: {str(e)}"
    
    def _erase_segment(self, segment: FirmwareSegment) -> Tuple[bool, str]:
        """
        擦除固件段
        
        擦除流程：
        1. 使用SET_MTA命令设置擦除起始地址（从S19文件中获取）
        2. 使用PROGRAM_CLEAR命令执行擦除，长度通过命令数据的后4字节传递（小端序）
        
        Args:
            segment: 固件段对象，包含起始地址和大小信息
            
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            # 第一步：设置内存传输地址(MTA) - 擦除起始地址
            # 该地址来源于S19文件中解析的段地址
            suc, msg = self._xcp.set_mta(segment.address)
            if not suc:
                return False, f"设置擦除起始地址失败: {msg}"
            
            # 第二步：发送程序清除命令
            # 擦除长度通过命令数据的后4字节传递，格式为小端序：
            # data[0] = 长度的低8位
            # data[1] = 长度的第二个8位  
            # data[2] = 长度的第三个8位
            # data[3] = 长度的高8位
            suc, msg = self._xcp.program_clear(segment.size)
            if not suc:
                return False, f"执行擦除命令失败: {msg}"
            time.sleep(1)
            return True, f"擦除成功 - 起始地址: 0x{segment.address:08X}, 擦除长度: {segment.size} bytes"
            
        except Exception as e:
            return False, f"擦除过程异常: {str(e)}"
    
    def _program_segment(self, segment: FirmwareSegment, processed_size: int, total_size: int) -> Tuple[bool, str]:
        """编程固件段"""
        try:
            # 设置内存传输地址
            suc, msg = self._xcp.set_mta(segment.address)
            if not suc:
                return False, msg
            
            # 分块编程数据
            # 使用PROGRAM_MAX命令，根据XcpCmdProgramMax函数，每次最多编程7字节数据（data[1-7]）
            data_len = 7
            chunk_size = min(g_config.download.chunk_size, data_len)
            data_offset = 0
            
            while data_offset < segment.size:
                # 计算本次传输的数据大小
                remaining = segment.size - data_offset
                current_chunk_size = min(chunk_size, remaining)
                
                # 获取数据块
                data_chunk = segment.data[data_offset:data_offset + current_chunk_size]
                
                # 编程数据块（使用PROGRAM命令）
                # suc, msg = self._xcp.program(data_chunk, data_len)
                suc, msg = self._xcp.program_max(data_chunk)
                if not suc:
                    return False, f"编程数据块失败: {msg}"
                
                data_str= [f"0x{num:02X}" for num in data_chunk]
                self.log.INFO(f"addr_start: {hex(segment.address + data_offset)}, len:{current_chunk_size} data: {data_str}")
                
                data_offset += current_chunk_size
                
                # 更新进度
                current_processed = processed_size + data_offset
                progress = 40.0 + current_processed * 50.0 / total_size
                self._update_progress(
                    progress, 
                    f"编程段{self._current_segment}/{self._total_segments} - {data_offset}/{segment.size}"
                )
                # time.sleep(0.005)
            
            return True, f"编程成功 - 地址: 0x{segment.address:08X}, 大小: {segment.size}"
            
        except Exception as e:
            return False, f"编程异常: {str(e)}"
        
    def _flash_done(self) -> Tuple[bool, str]:
        """
        编程完成
        """
        try:
            suc, msg = self._xcp.program([0]*4, 0)
            if not suc:
                return False, f"编程数据块失败: {msg}"
            self.log.INFO("编程完成")
            return True, "编程完成"
        except Exception as e:
            return False, f"编程完成异常: {str(e)}"

    def _verify_firmware(self) -> Tuple[bool, str]:
        """校验固件"""
        try:
            # 简单的校验实现 - 读取并比较部分数据
            segments = self._firmware_mgr.get_segments()
            
            for i, segment in enumerate(segments):
                # 只校验前几个字节
                verify_size = min(8, segment.size)
                
                # 设置内存传输地址
                suc, msg = self._xcp.set_mta(segment.address)
                if not suc:
                    return False, f"设置校验地址失败: {msg}"
                
                # 上传数据
                suc, uploaded_data, msg = self._xcp.upload(verify_size)
                if not suc:
                    return False, f"上传校验数据失败: {msg}"
                
                # 比较数据
                expected_data = segment.data[:verify_size]
                if uploaded_data != expected_data:
                    return False, f"校验失败 - 段{i+1}数据不匹配"
            
            return True, "固件校验成功"
            
        except Exception as e:
            return False, f"校验异常: {str(e)}"
    
    def get_download_progress(self) -> float:
        """获取下载进度"""
        return self._download_progress
    
    def get_firmware_info(self) -> Tuple[bool, dict, str]:
        """
        获取固件信息
        
        Returns:
            Tuple[bool, dict, str]: (是否成功, 固件信息, 消息)
        """
        try:
            if not self._firmware_mgr.is_loaded():
                return False, {}, "固件未加载"
            
            min_addr, max_addr = self._firmware_mgr.get_address_range()
            info = {
                "文件路径": self._firmware_mgr.get_loaded_file(),
                "段数量": self._firmware_mgr.get_segment_count(),
                "总大小": self._firmware_mgr.get_total_size(),
                "地址范围": f"0x{min_addr:08X} - 0x{max_addr:08X}",
                "段信息": []
            }
            
            # 添加每个段的信息
            segments = self._firmware_mgr.get_segments()
            for i, segment in enumerate(segments):
                seg_info = {
                    "段号": i + 1,
                    "起始地址": f"0x{segment.address:08X}",
                    "大小": segment.size,
                    "结束地址": f"0x{segment.address + segment.size - 1:08X}"
                }
                info["段信息"].append(seg_info)
            
            return True, info, "获取固件信息成功"
            
        except Exception as e:
            return False, {}, f"获取固件信息异常: {str(e)}"
    
    def _verify_checksum(self) -> Tuple[bool, str]:
        """
        校验固件checksum
        
        将上位机计算的checksum与MCU计算的checksum进行比较
        
        Returns:
            Tuple[bool, str]: (是否成功, 消息)
        """
        try:
            if not self._xcp.is_connected():
                return False, "XCP未连接"
            
            if not self._firmware_mgr.is_loaded():
                return False, "固件未加载"
            
            # 获取所有固件数据
            all_data = self._firmware_mgr.get_all_firmware_data()
            total_length = len(all_data)
            
            if total_length == 0:
                return False, "没有固件数据需要校验"
            
            # 计算上位机checksum
            pc_checksum = firm_checksum(all_data)
            
            # 设置MTA到固件起始地址
            segments = self._firmware_mgr.get_segments()
            if not segments:
                return False, "没有可用的固件段"
            
            # 使用第一个段的起始地址
            first_segment = sorted(segments, key=lambda seg: seg.address)[0]
            suc, msg = self._xcp.set_mta(first_segment.address)
            if not suc:
                return False, f"设置checksum起始地址失败: {msg}"
            
            # 发送BUILD_CHECKSUM命令获取MCU计算的checksum
            suc, mcu_checksum, msg = self._xcp.build_checksum(total_length)
            if not suc:
                return False, f"获取MCU checksum失败: {msg}"
            
            self.log.INFO(f"Checksum = PC: 0x{pc_checksum:08X}, MCU: 0x{mcu_checksum:08X}")
            
            # 比较checksum
            # if pc_checksum == mcu_checksum:
            #     return True, f"Checksum校验成功 - PC: 0x{pc_checksum:08X}, MCU: 0x{mcu_checksum:08X}"
            # else:
            #     return False, f"Checksum校验失败 - PC: 0x{pc_checksum:08X}, MCU: 0x{mcu_checksum:08X}"
            return True, ""
        except Exception as e:
            return False, f"Checksum校验异常: {str(e)}" 